import type { BracketLeftType } from "../base/Bracket/types";
import type { OperatorType } from "../base/Operator/types";
import type { expressionItemsType } from "./types";

import { Stack } from "../base/Stack";
import { isBracket, isOperator } from "./isInstanceOf";

/**
 * 将中缀表达式转为后缀表达式
 * @param infixArr 中缀表达式数组
 * @returns 后缀表达式数组
 */
function turnToSuffix(infixArr: Array<expressionItemsType>): Array<OperatorType | number> | never {
    // 1.申请一个暂存运算符的栈，一个放后缀表达式的暂存数组
    let OperatorStack = new Stack<OperatorType | BracketLeftType>();// 运算符、括号(只能是左括号)
    let ResultStack = new Stack<number | OperatorType>();//操作数、运算符

    // 2.遍历中缀表达式数组的元素
    infixArr.forEach(item => {

        if (typeof (item) === 'number' && Number.isFinite(item)) { // 操作数直接放入输出数组
            // item可能为NaN、Infinity，Number.isFinite会过滤掉，最终执行底部的抛出异常
            // console.log('toSuffix将被处理的item:' + item);
            ResultStack.push(item);
            return;
        }

        // 如果是操作符、括号
        // 如果是‘)’,将‘(’之后的操作符出栈放入后缀表达式中，最后把‘(’出栈
        if (isBracket(item, 'right')) {
            // 找到'('之前不断把栈顶的元素出栈，并放入输出数组中
            while (!OperatorStack.isEmpty() && !isBracket(OperatorStack.getPeek()!, 'left')) {
                ResultStack.push(OperatorStack.pop() as OperatorType);
            }
            // while中断若不是因为找到了左括号，而是栈空，报错
            if (OperatorStack.pop() === undefined) {
                throw new Error("中缀表达式中，找不到对应的左括号");
            };
            return;
        }

        if (isBracket(item, 'left')) {
            // 如果是’(‘,直接入后缀栈
            OperatorStack.push(item as BracketLeftType);
            return;
        }

        if (isOperator(item)) {
            // 不满足直接入栈的情况，就把栈顶元素出栈，并放到输出数组，直到满足条件不再执行
            while (
                !OperatorStack.isEmpty() /* 操作符栈不为空 */
                && !isBracket(OperatorStack.getPeek()!, 'left') /* 操作符栈顶不是左括号 */
                && (OperatorStack.getPeek() as OperatorType).getPriority() >= (item as OperatorType).getPriority()
            ) {
                ResultStack.push(OperatorStack.pop() as OperatorType)
            }
            // 满足入栈条件，item入操作符栈
            OperatorStack.push(item as OperatorType);
            return;
        }

        throw new Error("中缀表达式中含有非法元素");

    })
    // 3.清空操作符栈内剩余元素，放到后缀栈中
    // 取出剩余运算符元素到后缀表达式暂存数组中
    while (!OperatorStack.isEmpty()) {
        const operatorEle = OperatorStack.pop()!

        if (isBracket(operatorEle)) {
            throw new Error("中缀表达式中含有非法左括号");
        } else {
            ResultStack.push(operatorEle as OperatorType);
        }
    }

    // 4.返回后缀表达式结果
    return ResultStack.getItems();
}

/**
 * 
 * @param suffixArr 后缀表达式
 * @returns 计算后缀表达式的结果
 */
function computeSuffix(suffixArr: Array<OperatorType | number>): number | never {
    let tempArr: number[] = [];
    // 从左到右遍历后缀表达式
    for (let item of suffixArr) {

        // 如果被遍历到的是数字，直接暂存在数组里
        if (typeof (item) === 'number') {
            
            tempArr.push(item);
            // console.log('toResult遍历到数字：' + item + ' 放入数组：' + tempArr);
            continue;
        }

        // 如果遍历到的是运算符，取出暂存数组最后两个数字，进行运算
        if ((tempArr.length > 1) && isOperator(item)) {
            // 连续取出末尾两个元素，注意位置，最后一个元素是被运算的z
            let b = tempArr.pop()!;
            let a = tempArr.pop()!;
            // 把运算结果放回暂存数组中
            tempArr.push(item.action(a, b));// 计算每次结果,并暂存到数组中
            continue;
        }
        throw new Error("后缀表达式不符合规范，缺少操作数");
        
    }// for
    // console.log('toResult最终结果:' + tempArr);
    if (tempArr.length !== 1) {
        throw new Error('计算后缀表达式出错，缺少运算符')
    }
    return parseFloat(tempArr[0].toFixed(12));// 最后仅剩一个元素,
}

/**
 * 根据中缀表达式计算结果
 * @param infixArr 中缀表达式
 * @returns 计算结果
 */
export function computeExpression(infixArr: Array<expressionItemsType>): number | never {
    try {
        const suffixArr = turnToSuffix(infixArr);
        const result = computeSuffix(suffixArr);
        return result;
    } catch (error) {
        // console.log('\O',JSON.stringify(error));
        throw error;
    }
}
